#ifndef __MAX__HPP__
#define __MAX__HPP__
// Max
struct Max {
  static std::string name() { return "MAX"; }
  constexpr static int apply (const int l, const int h) { 
    return (l < h) ? h : l;
  }
};

template< class A, class B>
constexpr Expression<BinaryExpression<Expression<A>, Expression<B>, Max> >
max(Expression<A> const & a, Expression<B> const & b)
{
  typedef BinaryExpression <Expression<A>, Expression<B>, Max> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <int PLACE, class A, bool B>
constexpr Expression<BinaryExpression<Expression<A>, Term<PLACE, Var, B>, Max> >
max(Expression<A> const & a, Term<PLACE, Var, B> const & b)
{
  typedef BinaryExpression <Expression<A>, Term<PLACE, Var, B>, Max> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <int PLACE, bool A, class B>
constexpr Expression<BinaryExpression<Term<PLACE, Var, A>, Expression<B>, Max> >
max(Term<PLACE, Var, A> const & a, Expression<B> const & b)
{
  typedef BinaryExpression <Term<PLACE, Var, A>, Expression<B>, Max> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <int PLACEA, int PLACE, bool A, bool B>
constexpr Expression<BinaryExpression<Term<PLACEA, Var, A>, Term<PLACE, Var, B>, Max> >
max(Term<PLACEA, Var, A> const & a, Term<PLACE, Var, B> const & b)
{
  typedef BinaryExpression <Term<PLACEA, Var, A>, Term<PLACE, Var, B>, Max> ExprT;
  return Expression<ExprT>(ExprT(a,b));
}

// Can we do without this ? Specializations don't overload !
template <class A>
constexpr Expression<BinaryExpression<Expression<A>, ConstantTerm, Max> >
max(Expression<A> const & a, int const & b)
{
  typedef BinaryExpression <Expression<A>, ConstantTerm, Max> ExprT;
  return Expression<ExprT>(ExprT(a,ConstantTerm(b)));
}

// Can we do without this ? Specializations don't overload !
template <class B>
constexpr Expression<BinaryExpression<ConstantTerm, Expression<B>, Max> >
max(int const & a, Expression<B> const & b)
{
  typedef BinaryExpression <ConstantTerm, Expression<B>, Max> ExprT;
  return Expression<ExprT>(ExprT(ConstantTerm(a),b));
}


template <int PLACEA, int PLACE>
constexpr Expression<BinaryExpression<Expression<VariableTerm<PLACEA> >, Expression<VariableTerm<PLACE> >, Max> >
max(VariableTerm<PLACEA> const & a, VariableTerm<PLACE> const & b) {
  typedef BinaryExpression <Expression<VariableTerm<PLACEA> >, Expression<VariableTerm<PLACE> >, Max> ExprT;
  return Expression<ExprT>(ExprT(Expression<VariableTerm<PLACEA> >(a),Expression<VariableTerm<PLACE> >(b)));
}
template <int PLACE>
constexpr Expression<BinaryExpression<Expression<ConstantTerm>, Expression<VariableTerm<PLACE> >, Max> >
max(ConstantTerm const & a, VariableTerm<PLACE> const & b) {
  typedef BinaryExpression <Expression<ConstantTerm>, Expression<VariableTerm<PLACE> >, Max> ExprT;
  return Expression<ExprT>(ExprT(Expression<ConstantTerm>(a),Expression<VariableTerm<PLACE> >(b)));
}
template <int PLACE>
constexpr Expression<BinaryExpression<Expression<VariableTerm<PLACE> >, Expression<ConstantTerm>, Max> >
max(VariableTerm<PLACE> const & b, ConstantTerm const & a) {
  typedef BinaryExpression <Expression<VariableTerm<PLACE> >, Expression<ConstantTerm>, Max> ExprT;
  return Expression<ExprT>(ExprT(Expression<VariableTerm<PLACE> >(b), Expression<ConstantTerm>(a)));
}
constexpr Expression<BinaryExpression<Expression<ConstantTerm>, Expression<ConstantTerm>, Max> >
max(ConstantTerm const & b, ConstantTerm const & a) {
  typedef BinaryExpression <Expression<ConstantTerm>, Expression<ConstantTerm>, Max> ExprT;
  return Expression<ExprT>(ExprT(Expression<ConstantTerm>(b), Expression<ConstantTerm>(a)));
}


// // This is pretty interesting .. cannot express max(a, b) as -min(-a, -b)
// // because there is no unary operator and the outer - cancels out and
// // effectively creates a min. This is a shame because it cuts down the number
// // of necessary temaplates instantiations
// template<template <class> class AA, class A, template <class> class BB, class B>
// constexpr Expression<BinaryExpression<AA<A>, BB<B>, Min> >
// max(AA<A> const & a, BB<B> const & b)
// {
//   typedef BinaryExpression<AA<A>, BB<B>, Min>  ExprT;
//   return Expression<ExprT>(ExprT(-a, -b));
// }

// template<template <class> class AA, class A, class B>
// constexpr Expression<BinaryExpression<AA<A>, B, Min> >
// max(AA<A> const & a, B const & b)
// {
//   typedef BinaryExpression<AA<A>, B, Min>  ExprT;
//   return Expression<ExprT>(ExprT(-a, -b));
// }

// template<class A, template <class> class BB, class B>
// constexpr Expression<BinaryExpression<A, BB<B>, Min> >
// max(A const & a, BB<B> const & b)
// {
//   typedef BinaryExpression<A, BB<B>, Min>  ExprT;
//   return Expression<ExprT>(ExprT(-a, -b));
// }

// template<class A, class B>
// constexpr Expression<BinaryExpression<A, B, Min> >
// max(A const & a, B const & b)
// {
//   typedef BinaryExpression<A, B, Min>  ExprT;
//   return Expression<ExprT>(ExprT(-a, -b));
// }

#endif //__MAX__HPP__
